<!--
 * @Author: Wushiqi
 * @Descripttion: 手写call/apply/bind
 * @Date: 2021-04-23 15:31:16
 * @LastEditor: Wushiqi
 * @LastEditTime: 2021-04-27 11:34:43
-->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    // call
    // Function.prototype.myCall = function(context) {
    //   context = context || window // 如果context参数没传的话，默认为window
    //   context.fn = this // 给context创建一个fn属性，将值设置为需要调用的函数
    //   // 因为call可以传入多个参数作为调用函数的参数，所以需要将参数剥离出来
    //   const args = [...arguments].slice(1)
    //   const resule = context.fn(...args) // 调用函数
    //   delete context.fn // 将对象上的函数删掉
    //   return resule
    // }
    // Person = function() {
    //   this.name = 'wsq'
    // }
    // const Stu = function() {
    //   this.sayInfo = function(age, height) {
    //     console.log( `姓名：${this.name}， 年龄：${age}， 身高：${height}`);
    //   }
    // }
    // const stu = new Stu()
    // stu.sayInfo.myCall(Person, 24, 166) // 一个一个的参数

    // apply
    // Function.prototype.myApply = function(context) {
    //   if (typeof this !== 'function') {
    //     throw new Error(`${this} in not Function`)
    //   }
    //   context = context || window
    //   context.fn = this
    //   let result
    //   if (arguments[1]) {
    //     result = context.fn(...arguments[1])
    //   } else {
    //     result = context.fn()
    //   }
    //   delete context.fn
    //   return result
    // }
    // Person = function() {
    //   this.name = 'wsq'
    // }
    // const Stu = function() {
    //   this.sayInfo = function(age, height) {
    //     console.log( `姓名：${this.name}， 年龄：${age}， 身高：${height}`);
    //   }
    // }
    // const stu = new Stu()
    // stu.sayInfo.myApply(Person, [23, 156]) // 第二个参数是一个数组

    // bind
    Function.prototype.myBind = function(context) {
      if (typeof this !== 'function') {
        throw new Error(`${this} is not function`)
      }
      context = context || window
      // 返回一个绑定this的函数，这里需要先保存this
      const _this = this
      const args = [...arguments].slice(1)
      // 返回一个函数
      return function Fn() {
        // 判断是不是new出来的
        if (this instanceof Fn) {
          // 如果是new出来的
          // 返回一个空对象，且使创建出来的实例的__proto__指向_this.prototype，且完成函数柯里化
          return new _this(...args, ...arguments)
        } else {
          // 如果不是new出来的，改变this的指向，且完成函数柯里化
          return _this.apply(context, args.concat(...arguments))
        }
      }
    }
    Person = function() {
      this.name = 'wsq'
    }
    const Stu = function() {
      this.sayInfo = function(age, height) {
        console.log( `姓名：${this.name}， 年龄：${age}， 身高：${height}`);
      }
    }
    const stu = new Stu()
    const say = stu.sayInfo.myBind(Person, 23, 186) // say是bind返回的函数
    setTimeout(() => {
      say() // 调用say()后才开始执行
    },5000);
  </script>
</body>
</html>